#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <strings.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>

#include <sys/wait.h>

#include "Log.hpp"
#include "InetAddr.hpp"
#include "Thread.hpp"
#include "ThreadPool.hpp"
const static int backlog = 16;
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    USAGE_ERROR
};

using task_t = std::function<void()>;
class TcpServer;
//V2多线程时的封装
class ThreadData
{
public:
//注意InetAddr是自定义类型，拷贝构造函数是默认生成的浅拷贝，此处不影响
    ThreadData(int sockfd,InetAddr addr,TcpServer* tps):_sockfd(sockfd),_addr(addr)
    {
        self = tps;
    }

    ~ThreadData()
    {}

    int _sockfd;
    InetAddr _addr;
    TcpServer* self;
};

class TcpServer
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
        _listen_sockfd = -1;
    }

    void InitServer()
    {
        // 1.创建套接字文件描述符
        _listen_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            LOG(FATAL, "socket create error,errno:%d:%s\n", errno, strerror(errno));
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socker create success,sockfd:%d\n", _listen_sockfd);
        // 2.创建套接字
        struct sockaddr_in local;

        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        // 3.绑定
        int n = ::bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind errror,errno:%d:%s\n", errno, strerror(errno));
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");

        // 4.tcp面向链接，因此通信前要先建立链接，建立链接时，对于服务器，
        // tcp服务器一直处于监听状态
        n = ::listen(_listen_sockfd, backlog);
        if (n < 0)
        {
            LOG(FATAL, "listen errror,errno:%d:%s\n", errno, strerror(errno));
            exit(LISTEN_ERROR);
        }
        LOG(INFO, "listen success\n");
    }
    void Server(int sockfd,InetAddr addr)
    {
        LOG(DEBUG,"tcpserver get a new link,this is:%s:%d\n",addr.Ip().c_str(),addr.Port());

        std::string clinetmessage = "[" + addr.Ip()+":" +std::to_string(addr.Port())+"]#";
        while(1)
        {
            sleep(1);
            char buffer[1024];
            ssize_t n = read(sockfd,buffer,sizeof(buffer)-1);
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << clinetmessage<<buffer<<std::endl;

                std::string echo_string = "[server echo] ";
                echo_string += buffer;
                ::write(sockfd,echo_string.c_str(),echo_string.size());
            }
            else if(n == 0)
            {
                //读到了文件尾，在网络中意味着，客户端退出，关闭链接
                LOG(INFO,"%s quit\n",clinetmessage.c_str());
                break;
            }
            else
            {
                LOG(FATAL,"read error,errno:%d:%s\n", errno, strerror(errno));
            }

        }
        ::close(sockfd);
    }
    //成员函数，去掉this指针，但是又需要this指针
    static void* HandleTask(void * args)
    {
        pthread_detach(pthread_self());

        ThreadData* td = static_cast<ThreadData*>(args);
        //要让td执行Server（私有），得要有this指针

        td->self->Server(td->_sockfd,td->_addr);
        delete td;

        
    }
    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            
            struct sockaddr_in peer; // 可能会给服务器发消息的远端套接字
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_listen_sockfd, (struct sockaddr *)&peer, &len);
            if(sockfd < 0)
            {
                LOG(WARNING, "accept errror,errno:%d:%s\n", errno, strerror(errno));
                continue;
            }
            LOG(INFO, "accept success\n");
            InetAddr one_peer(peer);

            //和这一台远端机器通信
            //V0:1V1通信，服务器无法处理多个客户端的请求
            //Server(sockfd,InetAddr(one_peer));

            // //V1：多进程版本，多进程资源开销大，一般推荐多线程版本
            // //多进程下，由于父进程会等待子进程的返回，如果不作处理，本质上父进程在阻塞等待，子进程在处理请求
            // //这种情况下和单进程没有区别
            // //当然，你可以设置父进程非阻塞等待
            // //这里谈一种更加优雅的做法，就是创建孙子进程处理请求，而子进程被父进程回收，回收后，父进程就能继续去监听
            // //有没有客户端来发送请求，孙子进程变孤儿进程被系统领养，由系统释放
            // //这里需要对文件描述符做处理
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     //对于子进程，012被占用，3也是父进程拷贝下来的，但是子进程只需要sockfd的值，用来通信，因此
            //     //可以把子进程不用的3号文件描述符释放
            //     ::close(_listen_sockfd);
                
            //     if(fork() > 0)exit(0);
            //     //孙子进程
            //     Server(sockfd,peer);
            //     exit(0);
            // }
            // //父进程
            // //而父进程，只做监听，拿到sockfd,即可，此后不做处理
            // ::close(sockfd);
            // ::waitpid(id,nullptr,0);


            // //V2:多线程版本
            // //要让线程看到的参数有sockfd、和addr
            // //可以用类来封装
            // pthread_t id;
            // //传给HandleTask的是一个指针
            // ThreadData* td = new ThreadData(sockfd,InetAddr(one_peer),this);
            // pthread_create(&id,nullptr,HandleTask,(void*)td);//主线程要等待副线程，可以将线程分离

            //V3：线程池版本,
            //推荐当线程执行的函数较大时，不要使用线程池这样的池化技术，因为可能发生线程池内的线程都被用满的情况，
            //建议当任务过长的时候，不使用池化技术，而直接是多线程
            task_t t = std::bind(&TcpServer::Server,this,sockfd,InetAddr(one_peer));
            ThreadPool<task_t>::GetInstance()->Enqueue(t);
        }
        _isrunning = false;
    }

    ~TcpServer()
    {
        if(_listen_sockfd == -1)
        ;
        ::close(_listen_sockfd);
    }

private:
    int _listen_sockfd;
    uint16_t _port;

    bool _isrunning;
};